package org.neuedu.filter;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSONObject;
import jakarta.annotation.Resource;
import org.neuedu.JWT.JwtUtil;
import org.neuedu.common.ResultJson;
import org.neuedu.entity.Employee;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
@ConfigurationProperties(prefix = "login.ignore")
public class LoginFilter implements GlobalFilter, Ordered {
    @Resource(name="lzwredis")
    RedisTemplate<String, Object> redisTemplate;
    List<String> urls;

    public void setUrls(List<String> urls) {
        this.urls = urls;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        final ServerHttpRequest request = exchange.getRequest();
        final ServerHttpResponse response = exchange.getResponse();
        // 获取到当前的请求地址 然后和urls里面一一比对,如果有能匹配上的 说明当前请求是不需要任何校验的 直接通过
        // 获取当前地址需要通过uri
        final URI uri = request.getURI();
        // 获取当前地址
        String path = uri.getPath();
        // 在比较前需要先创建一个地址匹配器
        AntPathMatcher pathMatcher = new AntPathMatcher();
        // 循环urls
        for (String url : urls) {
            // 循环匹配 如果其中任何一个地址可以匹配直接通过
            if (pathMatcher.match(url, path)) {
                return chain.filter(exchange);
            }
        }
        // 如果没有匹配到白名单 则获取请求头的token
        List<String> tokenList = request.getHeaders().get("token");
        // 如果token没有拿到 说明未登录
        if (null == tokenList || tokenList.size() == 0) {
            return error(response, ResultJson.unlogin("未登录"));
        }
        // 如果有token 则拿出第一个来 (一个key允许对应多个值)
        String token = tokenList.get(0);
        try {
            // 解析token
            final Employee emp = JwtUtil.decoder(token);
            // 用id作为key
            String key = "loginId::" + emp.getId();
            // 到redis中查找还有没有
            if(!redisTemplate.hasKey(key)) {
                // 如果没有说明已经超过半小时了 需要重新登录
                return error(response, ResultJson.unlogin("登录超时"));
            }
            //  key的存活期需要从此刻开始计时半小时
            redisTemplate.expire(key, 3, TimeUnit.HOURS);
            // 获取query 自己拼接一个openId参数 传递到controller 让controller可以拿到当前登录的用户id
            String query = uri.getQuery();
            StringBuilder newQuery = new StringBuilder();
            if (StringUtils.isNotBlank(query)) {
                newQuery.append(query)
                        .append("&");
            }
            newQuery.append("openId=").append(emp.getId());
            // uri中query是只读的 无法对query替换 构建一个新的uri替代
            final URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(newQuery.toString()).build().toUri();
            // request中的uri也是只读的 没有办法set 所以要构建一个新的request
            final ServerHttpRequest newRequest = request.mutate().uri(newUri).build();
            // exchange中的request是只读的 没有办法set 所以要构建一个新的exchange
            final ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
            return chain.filter(newExchange);

        } catch (Exception ex) {
            // 如果解析失败了 所以token 伪造的
            ex.printStackTrace();
            return error(response, ResultJson.unlogin("非法参数"));
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
    private Mono<Void> error(ServerHttpResponse response, ResultJson resultJson) {
        final DataBuffer databuffer = response.bufferFactory().wrap(JSONObject.toJSONString(resultJson).getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Flux.just(databuffer));
    }
}
